# GF_PY3_CLASS_Wrapper_Requests_2_x.py
# Create by GF 2025-04-23 23:05

# Python 3 Standard Libraries.
import io
# ..................................................
import requests

# ##################################################

# Request                            | Response                                 | Explain
# -----------------------------------+------------------------------------------+-----------------------
# "Accept: application/octet-stream" | "Content-Type: application/octet-stream" | 通用二进制流 MIME 类型
# "Accept: text/plain"               | "Content-Type: text/plain"               | 通用文本 MIME 类型
#                                    | "Transfer-Encoding: chunked"             | 分块传输编码

# ##################################################

class set_url:

    def __init__(self):

        self.url = "http://127.0.0.1/api/doc"

    def update(self, url_str:str):

        """ 更新键值对到 url 中 """

        self.url = url_str
        # ..........................................
        return self  # 返回 self 以实现链式调用

class set_headers:

    def __init__(self):

        # 注意事项:
        # - 要将 JSON 数据通过 POST 发送给 Flask 客户端 (即服务端通过 flask.request.json() 获取 JSON 数据),
        # - 要求客户端请求头必须带有 "Content-Type: application/json"

        self.headers = {
            "Accept": "application/octet-stream",
            "Accept-Encoding": "gzip",
            "Content-Type": "application/json"
        }

#   def update(self, key, value):
#
#       """ 更新键值对到 headers 中 """
#
#       self.headers[key] = value
#       # ..........................................
#       return self  # 返回 self 以实现链式调用

    def update(self, *args, **kwargs):

        """ 更新键值对到 headers 中 """
        """ 支持 update(key=value) 和 update({'key': 'value'}) 两种调用方式 """

        if args and isinstance(args[0], dict):
            self.headers.update(args[0])
        # ..........................................
        self.headers.update(kwargs)
        # ..........................................
        return self  # 返回 self 以实现链式调用

# ##################################################

class set_json:

    # HTTP 请求目标:
    # - Content-Type: application/json
    # CURL 命令:
    # - curl -X -H "Content-Type: application/json" POST --data '{"username": "admin", "password": "123456"}' http://127.0.0.1/login
    # - curl -X -H "Content-Type: application/json" POST --data @payload.json http://127.0.0.1/login
    # Python 3 Requests 2 方法:
    # - >>> import requestst
    # - >>> payload = {"username": "admin", "password": "123456"}
    # - >>> response = requests.post("http://127.0.0.1/login", json=payload)

    def __init__(self):

        self.json = {"args": [],  # args => [arg1, arg2, ..., argn]
                     "stream": False}

    def update(self, *args, **kwargs):

        """ 更新键值对到 json 中 """
        """ 支持 update(key=value) 和 update({'key': 'value'}) 两种调用方式 """

        if args and isinstance(args[0], dict):
            self.json.update(args[0])
        # ..........................................
        self.json.update(kwargs)
        # ..........................................
        return self  # 返回 self 以实现链式调用

# ##################################################

class wrapper_requests_2_x(object):

    # Example:
    # >>> w_requests = wrapper_requests_2_x()
    # >>>
    # >>> w_requests.url.update("http://127.0.0.1/api/mysql/query")
    # >>> w_requests.headers.update({"Accept": "application/octet-stream"})
    # >>> w_requests.headers.update({"Accept-Encoding": "gzip"})
    # >>> w_requests.json.update({"args": ["SELECT * FROM example_table;"]})
    # >>> content_bytes = w_requests.post()

    def __init__(self):

        self.url:object     = set_url()
        self.headers:object = set_headers()
        self.json:object    = set_json()

    def post(self):

        if (self.headers.headers["Accept"] == "application/json" and \
                  self.json.json["stream"] == True):

            # 流式 (Stream) 接收
            response = requests.post(url = self.url.url, headers = self.headers.headers, json = self.json.json, stream=True)
            # ......................................
            StrIO = io.StringIO()  # 使用文本模式解码
            # ......................................
            Char_Count = 0
            for chunk in Response.iter_content(chunk_size=1024):
                if chunk:
                    StrIO.write(chunk.decode("utf-8"))
                    Char_Count = Char_Count + len(chunk)
            # ......................................
            return StrIO.getvalue()  # 返回 str 类型

        if (self.headers.headers["Accept"         ] == "application/octet-stream" and \
            self.headers.headers["Accept-Encoding"] == "gzip"):
            
            response = requests.head(url = self.url.url, headers = self.headers.headers, json = self.json.json)
            server_headers = response.headers  # 发送 HEAD 请求获取文件大小

            # 流式 (Stream) 接收 (Requests 2.x Will Automatically Process "gzip" and Automatically Decompress Upon Receipt)
            response = requests.post(url = self.url.url, headers = self.headers.headers, json = self.json.json, stream=True)
            # ......................................
            print("[DEBUG] Provided %s Bytes Length (Before Compression) ..." % str(server_headers["X-Content-Length"]))
            # ......................................
            BytIO = io.BytesIO()  # 使用字节模式解码
            # ......................................
            Bytes_Count = 0
            for chunk in response.iter_content(chunk_size=1024):
                if chunk:
                    BytIO.write(chunk)
                    Bytes_Count = Bytes_Count + len(chunk)
            # ......................................
            print("[DEBUG] Received %d/%s Bytes Length, Finished." % (Bytes_Count, str(server_headers["X-Content-Length"])))
            # ......................................
            return BytIO.getvalue()  # 返回 bytes 类型, 如 b'\xe4\xbd\xa0\xe5\xa5\...' (UTF-8 编码)

# EOF Signed by GF.
